;;; GNU Guix --- Functional package management for GNU
;;; Copyright © 2022 unwox <me@unwox.com>
;;;
;;; This file is part of GNU Guix.
;;;
;;; GNU Guix is free software; you can redistribute it and/or modify it
;;; under the terms of the GNU General Public License as published by
;;; the Free Software Foundation; either version 3 of the License, or (at
;;; your option) any later version.
;;;
;;; GNU Guix is distributed in the hope that it will be useful, but
;;; WITHOUT ANY WARRANTY; without even the implied warranty of
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;;; GNU General Public License for more details.
;;;
;;; You should have received a copy of the GNU General Public License
;;; along with GNU Guix.  If not, see <http://www.gnu.org/licenses/>.

(define-module (guixrus home services senpai)
  #:use-module (gnu home services)
  #:use-module (gnu home services shepherd)
  #:use-module (gnu services configuration)
  #:use-module (guix gexp)
  #:use-module (guix packages)
  #:use-module ((guixrus packages senpai) #:prefix rus:)

  #:export (home-senpai-service-type
            home-senpai-configuration
            home-senpai-pane-widths
            home-senpai-colors))

(define-maybe file-like)
(define-maybe string)
(define-maybe number)
(define-maybe boolean)

(define (serialize-maybe-number field-name val)
  (if (not (eq? val %unset-value))
      (string-append (symbol->string field-name) " " (number->string val) "\n")
      ""))

(define (serialize-string field-name val)
  (string-append (symbol->string field-name) " \"" val "\"\n"))

(define (serialize-cmd field-name val)
  (if (not (eq? val %unset-value))
      (string-append (symbol->string field-name) " " val "\n")
      ""))

(define (serialize-maybe-string field-name val)
  (if (not (eq? val %unset-value))
      (string-append (symbol->string field-name) " \"" val "\"\n")
      ""))

(define (serialize-maybe-boolean field-name val)
  (if (not (eq? val %unset-value))
      (string-append (symbol->string field-name) " " (if val "true" "false") "\n")
      ""))

(define (serialize-list field-name val)
  (if (not (null? val))
      (string-append (symbol->string field-name)
                     " "
                     (string-join
                      (map
                       (lambda (s) (string-append "\"" s "\""))
                       val)
                      " ")
                     "\n")
      ""))

(define (serialize-maybe-file-like field-name val)
  #~(if #$(not (eq? val %unset-value))
          (string-append #$(symbol->string field-name) " " #$val "\n")
          ""))

(define (serialize-home-senpai-pane-widths field-name val)
  #~(string-append #$(symbol->string field-name)
                   " {\n"
                   #$(serialize-configuration val home-senpai-pane-widths-fields)
                   "}\n"))

(define (serialize-home-senpai-colors field-name val)
  #~(string-append #$(symbol->string field-name)
                   " {\n"
                   #$(serialize-configuration val home-senpai-colors-fields)
                   "}\n"))

(define-configuration home-senpai-pane-widths
  (nicknames
   maybe-number
   "The number of cells that the column for nicknames occupies in the timeline.
By default, 14.")
  (channels
   maybe-number
   "The number of cells that the column for channels occupies on screen.  By
default, 16.  Use special value 0 to make the channel list horizontal.  If the
value is negative, the channel list will be horizontal by default and will take
the positive (opposite) width value when toggled with F7.")
  (members
   maybe-number
   "The number of cells that that the column for the list of channel members
occupies on screen. By default, 16.  Use special value 0 to disable.  If the
value is negative, the member list will be disabled by default and will take the
positive (opposite) width value when toggled with F8."))

(define-configuration home-senpai-colors
  (prompt
   maybe-string
   "The color for \">\"-prompt that appears in command mode.")
  (unread
   maybe-string
   "The foreground color for unread buffer names in buffer lists."))

(define-configuration home-senpai-configuration
  (senpai
   (file-like rus:senpai)
   "The @code{senpai} package to use."
   empty-serializer)
  (address
   (string "")
   "The address (host[:port]) of the IRC server.  senpai uses TLS connections by
default unless you specify tls option to be false.  TLS connections default to
port 6697, plain-text use port 6667.")
  (nickname
   (string "")
   "Your nickname, sent with a NICK IRC message. It mustn't contain spaces or
colons (:).")
  (realname
   maybe-string
   "Your real name, or actually just a field that will be available to others
and may contain spaces and colons.  Sent with the USER IRC message.  By default,
the value of nick is used.")
  (username
   maybe-string
   "Your username, sent with the USER IRC message and also used for SASL
authentication.  By default, the value of nick is used.")
  (password
   maybe-string
   "Your password, used for SASL authentication.  See also password-cmd.")
  (password-cmd
   maybe-string
   "Alternatively to providing your SASL authentication password directly in
plaintext, you can specify a command to be run to fetch the password at runtime.
This is useful if you store your passwords in a separate (probably encrypted)
file using `gpg` or a command line password manager such as pass or gopass.  If
a password-cmd is provided, the value of password will be ignored and the first
line of the output of password-cmd will be used for login."
   serialize-cmd)
  (channel
   (list '())
   "The list of channel names that senpai will automatically join at startup and
server reconnect.")
  (highlight
   (list '())
   "The list of keywords that will trigger a notification and a display
indicator when said by others.  This directive can be specified multiple times.
By default, senpai will use your current nickname.")
  (on-highlight-path
   maybe-file-like
   "The shell script to be executed when you are highlighted.  By default,
senpai looks for a highlight shell script at $XDG_CONFIG_HOME/senpai/highlight.
If no file is found at that path, and an alternate path is not provided,
highlight command execution is disabled.

Run @code{man 5 senpai} to see full details.")
  (pane-widths
   (home-senpai-pane-widths (home-senpai-pane-widths))
   "Configure the width of various UI panes.")
  (tls
   maybe-boolean
   "Enable TLS encryption.  Defaults to true.")
  (typings
   maybe-boolean
   "Send typing notifications which let others know when you are typing a
message.  Defaults to true.")
  (mouse
   maybe-boolean
   "Enable or disable mouse support.  Defaults to true.")
  (colors
   (home-senpai-colors (home-senpai-colors))
   "Settings for colors of different UI elements.

Colors are represented as numbers from 0 to 255 for 256 default terminal colors
respectively.  -1 has special meaning of default terminal color.  To use true
colors, #rrggbb notation is supported.")
  (debug
   maybe-boolean
   "Dump all sent and received data to the home buffer, useful for debugging.
Defaults to false."))

(define (home-senpai-configuration-file config)
  `(("senpai/senpai.scfg"
     ,(mixed-text-file
       "senpai.scfg"
       (serialize-configuration config home-senpai-configuration-fields)))))

(define (home-senpai-packages config)
  (list (home-senpai-configuration-senpai config)))

(define home-senpai-service-type
  (service-type
   (name 'home-senpai)
   (extensions
    (list (service-extension
           home-xdg-configuration-files-service-type
           home-senpai-configuration-file)
          (service-extension
           home-profile-service-type
           home-senpai-packages)))
   (default-value (home-senpai-configuration))
   (description "Install and configure the @code{senpai} IRC client.")))
